﻿using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.ComponentModel;

namespace VsTortoiseSVN
{
    /// <summary>
    /// Describes how Webupdate should behave.
    /// </summary>
    public enum WebupdateBehaviour
    {
        /// <summary>
        /// Never check for updates automatically.
        /// </summary>
        None,

        /// <summary>
        /// Check for updates when Visual Studio has started.
        /// </summary>
        Startup,

        /// <summary>
        /// Check for updates when the interval elapsed.
        /// </summary>
        Interval
    }

    /// <summary>
    /// Stores options for the Webupdate module.
    /// </summary>
    public class WebupdateOptions
    {
        /// <summary>
        /// Gets/sets when to check for updates.
        /// </summary>
        public WebupdateBehaviour Behaviour { get; set; }

        /// <summary>
        /// Gets/sets interval in days that must elapse to re-check for
        /// an update automatically. WebupdateBehaviour must be Interval.
        /// </summary>
        public int IntervalInDays { get; set; }

        /// <summary>
        /// Gets/sets last date/time when VsTortoise checked for updates.
        /// </summary>
        public DateTime LastCheck { get; set; }

        /// <summary>
        /// Gets/sets whether to check for beta versions.
        /// </summary>
        public bool ConsiderBeta { get; set; }
    }

    /// <summary>
    /// Stores options for the Diagnostics module.
    /// </summary>
    public class DiagnosticsOptions
    {
        /// <summary>
        /// Gets/sets whether to enable VsTortoise diagnostics.
        /// </summary>
        public bool Enabled { get; set; }
    }

    /// <summary>
    /// Helper to serialize options, since the Options class is a singleton
    /// and I didn't find a solution how to deserialize members of the Options class separately.
    /// </summary>
    public class OptionsSerializeHelper
    {
        public WebupdateOptions Webupdate { get; set; }
        public DiagnosticsOptions Diagnostics { get; set; }

        public OptionsSerializeHelper()
        {
            Webupdate = new WebupdateOptions();
            Diagnostics = new DiagnosticsOptions();
        }
    }

    /// <summary>
    /// Container for VsTortoise options.
    /// Use the "Instance" method to access options.
    /// </summary>
    public class Options
    {
        #region Singleton
        static Options _instance;

        /// <summary>
        /// Reference to the only Options instance application-wide.
        /// </summary>
        public static Options Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new Options();

                return _instance;
            }
        }
        #endregion

        /// <summary>
        /// Gets full path of the VsTortoise settings file.
        /// </summary>
        private string FullPath
        {
            get
            {
                string path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
                path = Path.Combine(path, "VsTortoise");
                path = Path.Combine(path, "settings.xml");
                return path;
            }
        }

        /// <summary>
        /// Gets/sets Webupdate options.
        /// </summary>
        public WebupdateOptions Webupdate { get; set; }

        /// <summary>
        /// Gets/sets Diagnostics options.
        /// </summary>
        public DiagnosticsOptions Diagnostics { get; set; }

        /// <summary>
        /// Constructor
        /// </summary>
        public Options()
        {
            Webupdate = new WebupdateOptions();
            Diagnostics = new DiagnosticsOptions();

            Reset();
        }

        /// <summary>
        /// Resets options to their default values.
        /// </summary>
        private void Reset()
        {
            Webupdate.Behaviour = WebupdateBehaviour.Interval;
            Webupdate.IntervalInDays = 1;
            Webupdate.LastCheck = new DateTime(1980, 12, 27);
            Webupdate.ConsiderBeta = false;

            Diagnostics.Enabled = false;
        }

        #region Events
        /// <summary>
        /// When webupdate completed, the OnWebupdateCompleted() method is called.
        /// </summary>
        public void OnWebupdateCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Webupdate.LastCheck = DateTime.Now;
        }
        #endregion

        #region Serialization

        /// <summary>
        /// Loads options from file. When option file could not be loaded, default values are used instead.
        /// </summary>
        public void Load()
        {
            LoadFrom(FullPath);
        }

        /// <summary>
        /// Loads options from the file specified by fullpath.
        /// </summary>
        private void LoadFrom(string fullpath)
        {
            System.IO.FileStream fileStream = null;

            try
            {
                fileStream = new System.IO.FileStream(fullpath, FileMode.Open);

                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(OptionsSerializeHelper));

                OptionsSerializeHelper options = (OptionsSerializeHelper)serializer.Deserialize(fileStream);

                Webupdate = options.Webupdate;
                Diagnostics = options.Diagnostics;
            }
            catch
            {
                Output.Instance.WriteLine(string.Format("Cannot load settings from '{0}'", fullpath));
                Reset();
            }
            finally
            {
                if (fileStream != null)
                    fileStream.Close();
            }
        }

        /// <summary>
        /// Saves options to file.
        /// </summary>
        public void Save()
        {
            SaveAs(FullPath);
        }

        /// <summary>
        /// Saves options to the file specified by fullpath.
        /// </summary>
        private void SaveAs(string fullpath)
        {
            System.IO.FileStream fileStream = null;

            try
            {
                string directory = Path.GetDirectoryName(fullpath);
                if (!Directory.Exists(directory))
                {
                    Output.Instance.WriteLine(string.Format("Creating directory '{0}'", fullpath));
                    Directory.CreateDirectory(directory);
                }

                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(OptionsSerializeHelper));

                OptionsSerializeHelper options = new OptionsSerializeHelper();
                options.Webupdate = Webupdate;
                options.Diagnostics = Diagnostics;

                fileStream = new System.IO.FileStream(fullpath, FileMode.Create);
                serializer.Serialize(fileStream, options);
            }
            catch
            {
                Output.Instance.WriteLine(string.Format("Cannot save settings to '{0}'", fullpath));
            }
            finally
            {
                if (fileStream != null)
                    fileStream.Close();
            }
        }
        #endregion

        /// <summary>
        /// Applies options to VsTortoise modules.
        /// </summary>
        public void Apply()
        {
            VsTortoiseSVN.Output.Instance.Enabled = Diagnostics.Enabled;

            VsTortoiseSVN.Webupdate.Instance.ConsiderBeta = Webupdate.ConsiderBeta;
        }
    }
}
